
#include <xs/vm.h>
#include <types.h>
#include <dynamic_objects.h>

using namespace xkp;

//execution_context
execution_context::execution_context(ByteCode code, variant _this, param_list* args):
  code_(code),
  instructions_(code->instructions),
  constants_(code->constants),
  pc_(0),
  jump_(false),
  this_(_this)
  {
    stack_.resize(1024); //td: arbitrary, inefficient
    if (args)
      {
        //bind parameters
        //td: type checking, named parameters, etc
        size_t sz = args->size();
        for(size_t i = 0; i < sz; i++)
          {
            stack_[i] = args->get(i);
          }
      }
  }

variant execution_context::execute()
  {
    size_t isz = instructions_.size();
    
    while(pc_ < isz)
      {
        instruction& i = instructions_[pc_];
        //td: a fuction table would be better, no?
        switch(i.id)
          {
            case i_jump:
              {
                pc_   = i.data.value;
                jump_ = true;
                break;
              }
              
            case i_jump_if:
              {
                bool control = operands_.top(); operands_.pop();
                if (control)
                  {
                    pc_   = i.data.value;
                    jump_ = true;
                  }
                break;
              }
              
            case i_jump_if_not:
              {
                bool control = operands_.top(); operands_.pop();
                if (!control)
                  {
                    pc_   = i.data.value;
                    jump_ = true;
                  }
                break;
              }
              
            case i_store:
              {
                stack_[i.data.value] = operands_.top(); 
                operands_.pop();
                break;
              }

            case i_load:
              {
                operands_.push(stack_[i.data.value]);
                break;
              }

            case i_load_constant:
              {
                operands_.push(constants_[i.data.value]);
                break;
              }

            case i_return:       return variant();
            case i_return_value: return operands_.top();
              
            case i_dup_top:  operands_.push( operands_.top() ); break;
            case i_pop:      operands_.pop();                   break;

            case i_binary_operator:
              {
                operator_exec* e      = operators_.get_operator(i.data.value); 
                variant        arg2   = operands_.top(); operands_.pop();
                variant        arg1   = operands_.top(); operands_.pop();
                variant        result = e->exec(arg1, arg2); 
                
                operands_.push(result);
                break;
              }
            case i_unary_operator:
              {
                operator_exec* e      = operators_.get_operator(i.data.value); 
                variant        arg1   = operands_.top(); operands_.pop();
                variant        result = e->exec(arg1, variant()); 
                
                operands_.push(result);
                break;
              }
            case i_load_this:
              {
                operands_.push( this_ );
                break;
              }  
            case i_call:
              {
                Executer call = operands_.top(); operands_.pop();
                param_list pl;
                for(int p = 0; p < i.data.call_data.param_count; p++)
                  {
                    pl.add(operands_.top()); operands_.pop();
                  }
                
                variant caller = operands_.top(); operands_.pop();                                  

                void*   caller_id;
                if (i.data.call_data.is_dynamic)
                  {
                    IDynamicObject* obj = caller; //td: catch type mismatch
                    caller_id = obj;
                  }
                else 
                  caller_id = caller.get_pointer();
                
                operands_.push( call->exec(caller_id, pl) );  
                break;
              }
            case i_this_call:
              {
                Executer call = operands_.top(); operands_.pop();
                param_list pl;
                for(int p = 0; p < i.data.call_data.param_count; p++)
                  {
                    pl.add(operands_.top()); operands_.pop();
                  }
                
                void* caller_id;
                if (i.data.call_data.is_dynamic)
                  {
                    IDynamicObject* obj = this_; //td: catch type mismatch
                    caller_id           = obj;
                  }
                else 
                  caller_id = this_.get_pointer();

                operands_.push( call->exec(caller_id, pl) );  
                break;
              }
            case i_get:
              {
                Getter  call   = operands_.top(); operands_.pop();
                variant caller = operands_.top(); operands_.pop();                                  

                void* caller_id;
                if (i.data.value)
                  {
                    IDynamicObject* obj = caller; //td: catch type mismatch
                    caller_id           = obj;
                  }
                else 
                  caller_id = caller.get_pointer();

                variant result = call->get(caller_id);  
                operands_.push(result);
                break;
              }
            case i_set:
              {
                Setter  call      = operands_.top(); operands_.pop();
                variant value     = operands_.top(); operands_.pop();                                  
                variant caller    = operands_.top(); operands_.pop();                                  

                void* caller_id;
                if (i.data.value)
                  {
                    IDynamicObject* obj = caller; //td: catch type mismatch
                    caller_id           = obj;
                  }
                else 
                  caller_id = caller.get_pointer();

                call->set(caller_id, value);  
                break;
              }
            case i_instantiate:
              {
                schema* type = operands_.top(); operands_.pop();
                param_list pl;
                for(int p = 0; p < i.data.value; p++)
                  {
                    pl.add(operands_.top()); operands_.pop();
                  }
                  
                variant result;
                if (type->create(result, &pl))
                  operands_.push(result);
                else
                  assert(false); //td: error, type does not instantiate
                break;
              }
            case i_dispatch:
              {
                event_info      ev     = operands_.top(); operands_.pop();
                IDynamicObject* caller = operands_.top(); operands_.pop();
                
                param_list pl;
                for(int p = 0; p < i.data.value; p++)
                  {
                    pl.add(operands_.top()); operands_.pop();
                  }
                
                caller->dispatch_event(ev.id, pl);
                break;
              }
            default:
              assert(false); //say wha
          }
        
        if (!jump_)
          pc_++;
          
        jump_ = false;
      }
      
    return variant();
  }

//code_setter
code_setter::code_setter(ByteCode _code, int var_idx):
  code_(_code),
  idx_(var_idx)
  {
  }
  
void code_setter::set( void* instance, const variant value )
  {
    IDynamicObject* self = static_cast<IDynamicObject*>(instance);

    if (idx_ >= 0)
      {
        //do the value assigment if there is an anonymous slot for it
        self->set_anonymous(idx_, value);
      }
    
    //td: pass the previous value  
    execution_context e(code_, self);
    e.execute();
  }

//code_getter
code_getter::code_getter(ByteCode _code):
  code_(_code)
  {
  }
  
variant code_getter::get(void* instance)
  {
    IDynamicObject* self = static_cast<IDynamicObject*>(instance);
    execution_context e(code_, self);
    return e.execute();
  }

//code_executer
code_executer::code_executer(ByteCode _code):
  code_(_code)
  {
  }
  
variant code_executer::exec(void* instance, const param_list args)
  {
    IDynamicObject* d = static_cast<IDynamicObject*>(instance);
    execution_context e(code_, d, const_cast<param_list*>(&args));
    return e.execute();
  }
  
